#include <cstdio>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include "projection.h"
#include "minIni.h"
#include "config.h"
#include "resources.h"
#include "map.h"
#include "spriteanimator.h"
#include "sprite.h"
#include "blitqueue.h"
#include "grcontext.h"

using namespace Oribi;

MapTile::MapTile():
    type(/*rand() % N_TERRAIN_TYPES*/ MAP_TILE_GRASS),
    height((rand() % 8) - 16/*0*/){}

Map::Map():
    size(0),
    tiles(NULL)
{
    sprites = new Sprite[N_TERRAIN_TYPES];
}

Map::Map(int s):
    size(s)
{
    allocateTiles();
    sprites = new Sprite[N_TERRAIN_TYPES];
}

Map::~Map()
{
    delete [] tiles;
    delete [] sprites;
}

void Map::allocateTiles(int s)
{
    if (!s)
        s = size;
    tiles = new MapTile[s * s];
}

void Map::initialise(int s)
{
    if (!size)
        size = s;

    if (tiles)
        delete [] tiles;

    allocateTiles();
}

SDL_Point tilePosition(int x, int y, int height)
{
    SDL_Point pos;
    pos.x = (x - y) * MapTile::xSizeHalf;
    pos.y = (x + y) * MapTile::ySizeHalf + height;
    return pos;
    //return projectToIsometric(x * MapTile::ySize, y * MapTile::xSize, height);
}

#define     MAP_SLICES 10
#define DBL_MAP_SLICES (2 * MAP_SLICES)
#define     MAP_SECTS  (DBL_MAP_SLICES * DBL_MAP_SLICES)

void Map::render(GraphicContext& gr)
{
    Sprite *sprite;
    int sectSize = size / DBL_MAP_SLICES;
    int k;
    SDL_Rect sections[MAP_SECTS], sprRect;
    SDL_Point sectPoint;

    for (int i = 0; i < DBL_MAP_SLICES; ++i)
    {
        for (int j = 0; j < DBL_MAP_SLICES; ++j)
        {
            k = i * DBL_MAP_SLICES + j;
            sections[k].x = i * sectSize;
            sections[k].y = j * sectSize;
            sections[k].w = sectSize;
            sections[k].h = sectSize;

            sectPoint = projectToIsometric(sections[k].x, sections[k].y, 0,
                                      MapTile::xSize, MapTile::ySize);

            if (gr.rectIntersectsFOV({sectPoint.x, sectPoint.y,
                                     sectSize * MapTile::xSizeHalf,
                                     sectSize * MapTile::ySizeHalf}));
            for (int y = sections[k].y; y < sections[k].y + sections[k].h; ++y)
            {
                for (int x = sections[k].x; x < sections[k].x + sections[k].w; ++x)
                {
                    int i = y * size + x;
                    sprite = &sprites[tiles[i].type];

                    sprite->position = tilePosition(x, y, tiles[i].height);
                    sprRect = sprite->getDestRect();

                    gr.addSpriteToBlitQueue(*sprite);
                }
            }
        }
    }
}

int Map::getTileHeight(int x, int y) const
{
    if (x < size && x >= 0
     && y < size && y >= 0)
        return tiles[y * size + x].height;
    else
        return 0;
}

bool Map::initSprites(Resources& res)
{
    SDL_Surface *surf = res.getSpriteSheet(Oribi::Resources::tileTextures);
    if (!surf)
        return false;
    for (int i = 0; i < N_TERRAIN_TYPES; ++i)
    {
        sprites[i] = {surf, {i * MapTile::xSize, 0, MapTile::xSize, MapTile::spriteHeight}};
        //sprites[i].base = {MapTile::xSizeHalf, MapTile::ySizeHalf};
    }

    return true;
}
